/*
	@(#)Configuration.java 1.0 2013-06-09
 */

package ftpproxy;

import java.net.*;
import java.util.*;

class Configuration {
	Properties properties;

	int bindPort;
	InetAddress bindAddress;

	// variables read from configuration file
	boolean onlyAuto;
	String autoHostname;
	int autoPort;
	String autoUserid;
	String autoPassword;
	String masqueradeHostname;
	boolean isUrlSyntaxEnabled;
	int[] serverBindPorts;
	int[] clientBindPorts;
	boolean serverOneBindPort, clientOneBindPort;
	boolean validateDataConnection;
	boolean debug;

	// Init Ftp Dir
	/*
	 * String ftpDir; String realFtpDir;
	 */
	String logDir;

	// lists of subnets
	List<Subnet> useActive, usePassive;
	List<Subnet> allowFrom, denyFrom, allowTo, denyTo;

	// messages
	String msgConnect;
	String msgConnectionRefused;
	String msgOriginAccessDenied;
	String msgDestinationAccessDenied;
	String msgIncorrectSyntax;
	String msgInternalError;
	String msgMasqHostDNSError;

	public Configuration(Properties properties) throws UnknownHostException {
		this.properties = properties;

		bindPort = getInt("bind_port", 8089);
		String ba = getString("bind_address");
		bindAddress = ba == null ? null : InetAddress.getByName(ba.trim());

		serverBindPorts = getPortRanges("server_bind_ports");
		clientBindPorts = getPortRanges("client_bind_ports");
		serverOneBindPort = serverBindPorts != null && serverBindPorts.length == 2
				&& serverBindPorts[0] == serverBindPorts[1];
		clientOneBindPort = clientBindPorts != null && clientBindPorts.length == 2
				&& clientBindPorts[0] == clientBindPorts[1];

		masqueradeHostname = getString("masquerade_host");
		if (masqueradeHostname != null) {
			// This is just to throw an UnknownHostException
			// if the config is incorrectly set up.
			InetAddress.getByName(masqueradeHostname.trim());
		}

		useActive = getSubnets("use_active");
		usePassive = getSubnets("use_passive");
		allowFrom = getSubnets("allow_from");
		denyFrom = getSubnets("deny_from");
		allowTo = getSubnets("allow_to");
		denyTo = getSubnets("deny_to");

		onlyAuto = getBool("only_auto", false);
		autoHostname = getString("auto_host");
		if (autoHostname != null) {
			autoHostname = autoHostname.trim();
		}
		autoPort = getInt("auto_port", 21);
		autoUserid = getString("auto_userid");
		autoPassword = getString("auto_password");

		isUrlSyntaxEnabled = getBool("enable_url_syntax", true);
		validateDataConnection = getBool("validate_data_connection", true);
		debug = getBool("output_debug_info", false);

		/*
		 * ftpDir = getString("ftp_dir"); realFtpDir =
		 * getString("real_ftp_dir");
		 */
		logDir = getString("log_dir");

		msgConnect = "220 " + getString("msg_connect", "Java FTP Proxy Server (usage: USERID=user@site) ready.");

		msgConnectionRefused = "421 " + getString("msg_connection_refused", "Connection refused, closing connection.");

		msgOriginAccessDenied = "531 " + getString("msg_origin_access_denied", "Access denied - closing connection.");

		msgDestinationAccessDenied = "531 "
				+ getString("msg_destination_access_denied", "Access denied - closing connection.");

		msgIncorrectSyntax = "531 " + getString("msg_incorrect_syntax", "Incorrect usage - closing connection.");

		msgInternalError = "421 " + getString("msg_internal_error", "Internal error, closing connection.");

		msgMasqHostDNSError = "421 "
				+ getString("msg_masqerade_hostname_dns_error", "Unable to resolve address for " + masqueradeHostname
						+ " - closing connection.");
	}

	public boolean getBool(String name, boolean defaultValue) {
		String value = getString(name);
		return value == null ? defaultValue : value.trim().equals("1");
	}

	public int getInt(String name, int defaultValue) {
		String value = properties.getProperty(name);
		properties.remove(name);
		return value == null ? defaultValue : Integer.parseInt(value.trim());
	}

	public String getString(String name) {
		return getString(name, null);
	}

	public String getString(String name, String defaultValue) {
		String value = properties.getProperty(name, defaultValue);
		properties.remove(name);
		return value;
	}

	public List<Subnet> getSubnets(String name) {
		String s = getString(name);
		if (s == null)
			return null;

		List<Subnet> list = new LinkedList<Subnet>();
		StringTokenizer st = new StringTokenizer(s.trim(), ",");
		while (st.hasMoreTokens()) {
			list.add(new Subnet(st.nextToken().trim()));
		}

		return list;
	}

	/**
	 * Returns an array of length 2n, where n is the number of port ranges
	 * specified. Index 2i will contain the first port number in the i'th range,
	 * and index 2i+1 will contain the last. E.g. the string
	 * "111,222-333,444-555,666" will result in the following array: {111, 111,
	 * 222, 333, 444, 555, 666, 666}
	 */
	public int[] getPortRanges(String name) {
		String s = getString(name);
		if (s == null)
			return null;

		StringTokenizer st = new StringTokenizer(s.trim(), ",");
		int[] ports = new int[st.countTokens() * 2];

		if (ports.length == 0)
			return null;

		int lastPort = 0;
		for (int p = 0; st.hasMoreTokens(); p += 2) {
			String range = st.nextToken().trim();
			int i = range.indexOf('-');

			if (i == -1) {
				ports[p] = ports[p + 1] = Integer.parseInt(range);
			} else {
				ports[p] = Integer.parseInt(range.substring(0, i));
				ports[p + 1] = Integer.parseInt(range.substring(i + 1));
			}
			if (ports[p] < lastPort || ports[p] > ports[p + 1]) {
				throw new RuntimeException("Ports should be listed in increasing order.");
			}
			lastPort = ports[p + 1];
		}

		return ports;
	}
}
